#include<iostream>
using namespace std;

const int N = 50010;
int n, k, res;
int p[N]; //父节点
int d[N]; //到父节点的距离

int find(int x)//带权并查集模板find
{
    if(p[x] != x)
    {
        int t = find(p[x]); //先把父节点及以上压缩到树根
        d[x] += d[p[x]]; //更新边权
        p[x] = t; //x节点也压缩到树根
    }
    return p[x];
}
int main()
{
    cin >> n >> k;//输入数据
    for(int i = 1; i <= n; i++) p[i] = i;
    while(k--)
    {
        int v, x, y;
        cin >> v >> x >> y;//输入边权值
        if(x > n || y > n) res++;//对边权值进行特判
        else
        {
            int rx = find(x), ry = find(y);
            if(v == 1)
            {
                //假话
                if(rx == ry && (d[y] - d[x]) % 3) res++;
                //真话
                else if(rx != ry)  //当前不在同一集合中，无法判定为假。故为真，应加入同一集合表示存在同类关系
                {
                    p[rx] = ry;
                    d[rx] = d[y] - d[x]; //(d[x]+d[rx]-d[y])%3 = 0,由于判断时都针对mod 3,故3可省略
                }
            }
            else  //x吃y
            {
                if(x == y) res++;
                else if(rx == ry && (d[x] - d[y] - 1) % 3) res++; //C++中负数取模得非正数，需要注意别写错
                else if(rx != ry)
                {
                    p[rx] = ry;
                    d[rx] = d[y] + 1 - d[x];
                }
            }
        }
    }
    cout << res << endl;
}

